package com.tools.web.interceptor.auth._common;

import com.tools.common.object.Note;
import com.tools.common.object.json.JsonKit;
import com.tools.common.thread.Timeunit;
import com.tools.web.interceptor.auth.*;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.ReturnType;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.Map;
import java.util.Objects;

/**
 * RedisTemplate&lt;String, String&gt; 客户端操作 Redis 实现的 “用户多在线记录” 的 AuthenticationManager 身份认证拦截器
 * */
@Note("RedisTemplate<String, String> 客户端操作 Redis 实现的 “用户多在线记录” 的 AuthenticationManager 身份认证拦截器")
public abstract class RedisTemplateMultipleAuthenticationManager extends GenericAuthenticationManager implements MultipleRedisLuaScript {

    @Note("操作 Redis 的 RedisTemplate<String, String> 客户端")
    private final RedisTemplate<String, String> redisTemplate;

    protected RedisTemplateMultipleAuthenticationManager(RedisTemplate<String, String> redisTemplate, AuthSource source, AuthUserService authUserService) {
        super(source, authUserService);
        this.redisTemplate = Objects.requireNonNull(redisTemplate, "操作 Redis 的 RedisTemplate<String, String> 客户端为 null");
    }

    @Override
    public final Timeunit getTimeunitUsedBySubclass() {
        return Timeunit.SECOND;
    }

    @Override
    protected GetAuthUserResult getAuthUser(String key, AuthToken decryptedAuthToken) {
        String uuid = decryptedAuthToken.getUuid();
        String loginDeviceType = decryptedAuthToken.getLoginDeviceType();
        String tokenKey = super.getMultipleLoginRecordSaveKey(key, loginDeviceType, uuid);
        String authUserKey = super.getMultipleAuthUserSaveKey(key);
        Object result = this.redisTemplate.execute((RedisConnection connection) -> connection.eval(
                GET_AUTH_USER.getBytes(),
                ReturnType.VALUE,
                2,
                tokenKey.getBytes(),
                authUserKey.getBytes()
        ));
        if (result == null) return super.protected_getResultIfExpired(decryptedAuthToken);
        String json = result.toString();
        MultipleAuthUser authUser = JsonKit.toEntity(json, MultipleAuthUser.class);
        authUser.setLoginUserType(decryptedAuthToken.getLoginUserType());
        authUser.setLoginDeviceType(loginDeviceType);
        authUser.setUuid(uuid);
        return new GetAuthUserResult(true, authUser);
    }


    @Override
    protected void setAuthUser(String key, AuthUser srcAuthUser, AuthToken decryptedAuthToken) {
        String loginDeviceType = decryptedAuthToken.getLoginDeviceType();
        String tokenKey = super.getMultipleLoginRecordSaveKey(key, loginDeviceType,
                decryptedAuthToken.getUuid());
        String deviceKeyPrefix = super.getMultipleDeviceSavePrefix(key, loginDeviceType);
        Map<String, Integer> loginDeviceTypeAndMaxOnlineTotal = super.getLoginDeviceTypeAndMaxOnlineTotal();
        int maxSize = (loginDeviceTypeAndMaxOnlineTotal != null && !loginDeviceTypeAndMaxOnlineTotal.isEmpty())
                ? loginDeviceTypeAndMaxOnlineTotal.get(loginDeviceType)
                : 0;
        long loginTime = System.currentTimeMillis();
        String authUserKey = super.getMultipleAuthUserSaveKey(key);
        String authUserValue = JsonKit.toJSON(srcAuthUser);
        long expirationMill = decryptedAuthToken.getExpirationMill();
        long expiration = this.getTimeunitUsedBySubclass().cast(expirationMill, Timeunit.MILL);
        this.redisTemplate.execute((RedisConnection connection) -> connection.eval(
                SET_AUTH_USER.getBytes(),
                ReturnType.VALUE,
                0,
                deviceKeyPrefix.getBytes(), String.valueOf(maxSize).getBytes(),
                tokenKey.getBytes(), String.valueOf(loginTime).getBytes(),
                String.valueOf(expiration).getBytes(),
                authUserKey.getBytes(), authUserValue.getBytes()
        ));
    }


    @Override
    protected void removeAuthUser(String key) {
        this.redisTemplate.execute((RedisConnection connection) -> connection.eval(
                REMOVE_AUTH_USER.getBytes(),
                ReturnType.INTEGER,
                0,
                key.getBytes()
        ));
    }

    @Override
    protected void updateAuthUser(String key, AuthUser newAuthUser) {
        String authUserKey = super.getMultipleAuthUserSaveKey(key);
        String json = this.redisTemplate.opsForValue().get(authUserKey);
        if(json == null) return;
        MultipleAuthUser oldAuthUser = JsonKit.toEntity(json, MultipleAuthUser.class);
        Object user = newAuthUser.getUser();
        oldAuthUser.setUser(user);
        newAuthUser.copyAuthData(oldAuthUser);
        newAuthUser.copyCustomData(oldAuthUser);
        String authUserValue = JsonKit.toJSON(oldAuthUser);
        this.redisTemplate.execute((RedisConnection connection) -> connection.eval(
                UPDATE_AUTH_USER.getBytes(),
                ReturnType.INTEGER,
                0,
                authUserKey.getBytes(),
                authUserValue.getBytes()
        ));
    }

    @Override
    protected void protected_loginOut(String key, String loginDeviceType, String uuid) {
        String tokenKey = super.getMultipleLoginRecordSaveKey(key, loginDeviceType, uuid);
        String authUserKey = super.getMultipleAuthUserSaveKey(key);
        this.redisTemplate.execute((RedisConnection connection) -> connection.eval(
                LOGIN_OUT.getBytes(),
                ReturnType.VALUE,
                0,
                tokenKey.getBytes(),
                key.getBytes(),
                authUserKey.getBytes()
        ));
    }
}
